package com.sikulifang.bank.simulation.service.bank;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.sql.Condition;
import cn.hutool.db.sql.SqlBuilder;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sikulifang.bank.simulation.constant.CodeConstants;
import com.sikulifang.bank.simulation.dto.ReceiptDTO;
import com.sikulifang.bank.simulation.dto.TransferPaymentDTO;
import com.sikulifang.bank.simulation.dto.cgb.CgbDetailDTO;
import com.sikulifang.bank.simulation.dto.cgb.CgbPayDTO;
import com.sikulifang.bank.simulation.dto.cgb.CgbQueryBalanceDTO;
import com.sikulifang.bank.simulation.enums.BankEnum;
import com.sikulifang.bank.simulation.util.PdfUtil;
import com.sikulifang.bank.simulation.util.R;
import com.sikulifang.frequency.core.InterfaceTypeEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;

import javax.xml.xpath.XPathConstants;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.sikulifang.bank.simulation.constant.CgbConstants.DOW_RECEIPT;
import static com.sikulifang.bank.simulation.constant.CgbConstants.PAY;
import static com.sikulifang.bank.simulation.constant.CgbConstants.PAY_ROLL;
import static com.sikulifang.bank.simulation.constant.CgbConstants.QUERY_BALANCE;
import static com.sikulifang.bank.simulation.constant.CgbConstants.QUERY_DETAIL;
import static com.sikulifang.bank.simulation.constant.CgbConstants.QUERY_PAY;
import static com.sikulifang.bank.simulation.constant.CgbConstants.QUERY_PAY_ROLL;
import static com.sikulifang.bank.simulation.constant.CgbConstants.QUERY_PAY_STATUS;
import static com.sikulifang.bank.simulation.constant.CgbConstants.SINGLE_PAY_NOT_SAME;
import static com.sikulifang.bank.simulation.constant.CgbConstants.SINGLE_PAY_SAME;
import static com.sikulifang.bank.simulation.constant.TableConstants.DETAIL_TABLE_NAME;

/**
 * ClassName CGBAccountServiceImplImpl
 * Description 广发
 *
 * @author Yat
 * Date 2023/6/7 15:27
 * version 1.0
 */
@Slf4j
@Service
public class CGBBankMessage extends AbstractBankMessage {

    @Override
    public String inlet(String message) {
        String action = getActionName(message);

        Map<String, Object> xmlToMap = XmlUtil.xmlToMap(message.trim());
        JSONObject messageData = JSONUtil.parseObj(xmlToMap.get("Message"));

        JSONObject bodyData = messageData.getJSONObject("Body");

        return switch (action) {
            case QUERY_BALANCE -> balance(action, bodyData);
            case QUERY_DETAIL -> detail(action, bodyData);

            case PAY -> pay(action, bodyData);
            case QUERY_PAY, QUERY_PAY_ROLL -> queryPay(action, bodyData);

            case SINGLE_PAY_SAME, SINGLE_PAY_NOT_SAME -> singlePay(action, messageData);
            case QUERY_PAY_STATUS -> querySinglePay(action, messageData);

            case PAY_ROLL -> payRoll(action, bodyData);
            case DOW_RECEIPT -> dowReceipt(action, messageData);
            default -> action;
        };
    }

    @SneakyThrows
    private String querySinglePay(String action, Map<String, Object> bodyData) {
        JSONObject head = JSONUtil.parseObj(bodyData.get("commHead"));
        String entSeqNo = head.getStr("entSeqNo");
        String tranDate = head.getStr("tranDate");

        JSONObject body = JSONUtil.parseObj(bodyData.get("Body"));

        SqlBuilder builder = SqlBuilder.create()
                .select()
                .from(DETAIL_TABLE_NAME)
                .where(new Condition("serial_no", body.getStr("origEntseqno")),
                        new Condition("tran_date", body.getStr("origEntdate")),
                        new Condition("flag", "D")
                );

        Entity entity = db.queryOne(builder.build(), builder.getParamValueArray());

        if (null == entity) {
            return errorTemplate("查询的明细不存在");
        }
        Dict dict = new Dict();
        dict.putAll(entity);

        if (entity.getInt("status") == 0) {
            dict.set("hostStatus", 8);
        } else if (entity.getInt("status") == 1) {
            dict.set("hostStatus", 6);
        } else {
            dict.set("hostStatus", 7);
        }

        dict.set("entSeqNo", entSeqNo);
        dict.set("tranDate", tranDate);

        return getTemplate(action, dict);

    }

    private String singlePay(String action, Map<String, Object> bodyData) {
        List<TransferPaymentDTO> array = new ArrayList<>();

        JSONObject head = JSONUtil.parseObj(bodyData.get("commHead"));
        String entSeqNo = head.getStr("entSeqNo");
        String tranDate = head.getStr("tranDate");

        JSONObject body = JSONUtil.parseObj(bodyData.get("Body"));

        TransferPaymentDTO paymentDTO = getTransferPaymentDTO(
                body.getStr("outAcc"), entSeqNo, "", entSeqNo,
                body.getStr("inAccName"), "1", body.getStr("inAcc"),
                body.getStr("inAccBank"), body.getStr("Remark"),
                body.getStr("Comment"), body.getStr("amount")
        );

        array.add(paymentDTO);

        R<Dict> dictR = tradingEntrance(array);
        List<Dict> errorList = (List<Dict>) dictR.getData().get("error");

        Dict dict = new Dict();
        dict.set("entSeqNo", entSeqNo);
        dict.set("tranDate", tranDate);

        dict.set("outAccName", body.getStr("outAccName"));
        dict.set("outAcc", body.getStr("outAcc"));
        dict.set("inAcc", body.getStr("inAcc"));
        dict.set("inAccName", body.getStr("inAccName"));
        dict.set("inAccBank", body.getStr("inAccBank"));
        dict.set("amount", body.getStr("amount"));

        String statusText = "";
        if (null != errorList) {
            statusText = errorList.get(0).getStr("statusText");
        }
        dict.set("errMsg", statusText);

        return getTemplate(action, dict);

    }

    private String payRoll(String action, JSONObject bodyData) {
        CgbPayDTO pay = BeanUtil.toBean(bodyData, CgbPayDTO.class);
        // -付款账号
        String payAccountNo = pay.getAccountNo();

        // 批量流水号
        String customerBatchNo = pay.getCustomerBatchNo();
        List<TransferPaymentDTO> array = new ArrayList<>();
        for (CgbPayDTO.RecordsDTO.RecordDTO recordDTO : pay.getRecords().getRecord()) {
            String bankCode = recordDTO.getBankCode();
            String customerSalarySeq = recordDTO.getCustomerSalarySeq();
            String inaccname = recordDTO.getEmployeeName();
            String transType = recordDTO.getTransType();
            String inacc = recordDTO.getEmployeeAccount();
            String inaccbank = recordDTO.getBankName();
            String remark = recordDTO.getRemark();
            String comment = recordDTO.getComment();
            String salary = recordDTO.getSalary();

            TransferPaymentDTO paymentDTO = getTransferPaymentDTO(
                    payAccountNo, customerBatchNo, bankCode, customerSalarySeq,
                    inaccname, transType, inacc,
                    inaccbank, remark, comment, salary
            );
            array.add(paymentDTO);
        }

        R<Dict> dictR = tradingEntrance(array);

        Dict dict = new Dict();
        dict.set("customerBatchNo", customerBatchNo);
        dict.set("accountNo", payAccountNo);
        dict.set("allCount", pay.getRecords().getRecord().size());
        dict.set("allSalary", pay.getAllSalary());

        List<Dict> errorList = new ArrayList<>();
        if (CodeConstants.SUCCESS == dictR.getCode()) {
            errorList = (List<Dict>) dictR.getData().get("error");
            dict.set("allErrCount", errorList.size());
            dict.set("list", errorList);
        } else {
            dict.set("allErrCount", pay.getRecords().getRecord().size());
            for (CgbPayDTO.RecordsDTO.RecordDTO recordDTO : pay.getRecords().getRecord()) {
                String customerSalarySeq = recordDTO.getCustomerSalarySeq();
                Dict error = new Dict();
                error.set("statusText", dictR.getMsg());
                error.set("serialNo", customerSalarySeq);
                errorList.add(error);
            }
            dict.set("list", errorList);
        }
        return getTemplate(action, dict);
    }

    @SneakyThrows
    private String queryPay(String action, JSONObject bodyData) {

        int errorCount = 0;
        int count = 0;
        int unknowCount = 0;
        BigDecimal allSalary = new BigDecimal("0.00");
        String accountNo = "";
        List<Dict> list = new ArrayList<>();

        String customerBatchNo = bodyData.getStr("customerBatchNo");

        Entity entity = Entity.create(DETAIL_TABLE_NAME);
        entity.set("flag", "D");
        entity.set("batch_serial_no", customerBatchNo);

        List<Entity> data = db.find(entity);

        if (!data.isEmpty()) {
            accountNo = data.get(0).getStr("account_no");

            for (Entity datum : data) {
                Dict dict = new Dict();
                dict.set("customerSalarySeq", datum.getStr("serial_no"));
                dict.set("serialNo", datum.getStr("serial_no"));
                // 行内汇款：  6：主机交易成功   7：主机交易失败  8：状态未知，没有收到后台系统返回的应答
                // 跨行汇款： A：支付系统正在处理  B：处理成功 C：处理失败 D：状态未知（没有此状态） E：大额查证 9：查证取消交易 b:汇款失败已冲账（没有此状态）
                String bankstatus = "8";
                String flag = "3";
                Integer status = datum.getInt("status");
                switch (status) {
                    case 0 -> {
                        bankstatus = "6";
                        flag = "4";
                        unknowCount++;
                    }
                    case 1 -> {
                        bankstatus = "7";
                        flag = "1";
                        count++;
                    }
                    case 2 -> {
                        bankstatus = "A";
                        flag = "0";
                        errorCount++;
                    }
                    default -> unknowCount++;
                }
                dict.set("bankstatus", bankstatus);
                dict.set("ewpSequence", "");
                dict.set("ewpCheckcode", "");
                dict.set("errorreason", datum.getStr("status_text"));

                // 0-失败       1-成功       3-状态未明       4-交易处理中
                dict.set("flag", flag);
                dict.set("salary", datum.getBigDecimal("money"));

                list.add(dict);
            }
        }

        Dict dict = new Dict();
        dict.set("customerBatchNo", customerBatchNo);
        dict.set("accountNo", accountNo);
        dict.set("allCount", data.size());
        dict.set("allSalary", allSalary);
        dict.set("allHandlefee", data.size());
        dict.set("errCount", errorCount);
        dict.set("count", count);
        dict.set("unknowCount", unknowCount);
        dict.set("list", list);
        return getTemplate(action, dict);
    }

    private String pay(String action, JSONObject bodyData) {
        CgbPayDTO pay = BeanUtil.toBean(bodyData, CgbPayDTO.class);
        // -付款账号
        String payAccountNo = pay.getAccountNo();

        // 批量流水号
        String customerBatchNo = pay.getCustomerBatchNo();
        List<TransferPaymentDTO> array = new ArrayList<>();

        for (CgbPayDTO.RecordsDTO.RecordDTO recordDTO : pay.getRecords().getRecord()) {
            String bankCode = recordDTO.getBankCode();
            String customerSalarySeq = recordDTO.getCustomerSalarySeq();
            String inaccname = recordDTO.getInaccname();
            String transType = recordDTO.getTransType();
            String inacc = recordDTO.getInacc();
            String inaccbank = recordDTO.getInaccbank();
            String remark = recordDTO.getRemark();
            String comment = recordDTO.getComment();
            String salary = recordDTO.getSalary();

            TransferPaymentDTO paymentDTO = getTransferPaymentDTO(
                    payAccountNo, customerBatchNo, bankCode, customerSalarySeq,
                    inaccname, transType, inacc,
                    inaccbank, remark, comment, salary
            );
            array.add(paymentDTO);
        }

        R<Dict> dictR = tradingEntrance(array);

        Dict dict = new Dict();
        dict.set("customerBatchNo", customerBatchNo);
        dict.set("accountNo", payAccountNo);
        dict.set("allCount", pay.getRecords().getRecord().size());
        dict.set("allSalary", pay.getAllSalary());

        List<Dict> errorList = new ArrayList<>();
        if (CodeConstants.SUCCESS == dictR.getCode()) {
            errorList = (List<Dict>) dictR.getData().get("error");
            dict.set("allErrCount", errorList.size());
            dict.set("list", errorList);
        } else {
            dict.set("allErrCount", pay.getRecords().getRecord().size());
            for (CgbPayDTO.RecordsDTO.RecordDTO recordDTO : pay.getRecords().getRecord()) {
                String customerSalarySeq = recordDTO.getCustomerSalarySeq();
                Dict error = new Dict();
                error.set("statusText", dictR.getMsg());
                error.set("serialNo", customerSalarySeq);
                errorList.add(error);
            }
            dict.set("list", errorList);
        }
        return getTemplate(action, dict);
    }

    private TransferPaymentDTO getTransferPaymentDTO(String payAccountNo, String customerBatchNo, String bankCode,
                                                     String customerSalarySeq,
                                                     String inaccname, String transType, String inacc,
                                                     String inaccbank, String remark,
                                                     String comment, String salary) {
        TransferPaymentDTO dto = new TransferPaymentDTO();

        if (transType.equals("0")) {
            transType = "2";
        }

        dto.setSerialNo(customerSalarySeq);
        dto.setBatchSerialNo(customerBatchNo);
        dto.setPayFlg(transType);

        dto.setPayAccountNo(payAccountNo);
        dto.setRecAccountNo(inacc);
        dto.setRecAccountName(inaccname);
        dto.setRecOpenBankCode(bankCode);
        dto.setRecOpenBankName(inaccbank);
        dto.setMoney(new BigDecimal(salary));
        dto.setPostScript(comment);
        dto.setSummary(remark);
        dto.setBankCode(bankCode());

        return dto;
    }

    @Override
    public String bankCode() {
        return BankEnum.CGB.name();
    }

    /**
     * 查余额
     */
    private String balance(String action, JSONObject obj) {
        CgbQueryBalanceDTO balance = BeanUtil.toBean(obj, CgbQueryBalanceDTO.class);
        // 获取账号
        String account = balance.getAccount();

        R<Dict> data = queryBalance(account);

        return getTemplate(action, data.getData());
    }

    private String getTemplate(String templateName, Dict dict) {
        return templateUtils.getTemplate(templateName, dict, BankEnum.CGB);
    }

    @SneakyThrows
    private String detail(String action, JSONObject obj) {
        CgbDetailDTO detail = BeanUtil.toBean(obj, CgbDetailDTO.class);

        if (StrUtil.hasBlank(
                detail.getAccount(), detail.getBeginDate(),
                detail.getEndDate(), detail.getQueryType()
        )) {
            return errorTemplate("必填参数不能为空");
        }

        SqlBuilder builder = SqlBuilder.create()
                .select()
                .from(DETAIL_TABLE_NAME)
                .where(new Condition("account_no", detail.getAccount()),
                        new Condition("tran_date", ">=", detail.getBeginDate()),
                        new Condition("tran_date", "<=", detail.getEndDate())
                );

        List<Entity> query = db.query(builder.build(), builder.getParamValueArray());
        Dict queryList = new Dict().set("queryList", query);
        queryList.set("account", detail.getAccount());
        queryList.set("size", query.size());


        return getTemplate(action, queryList);

    }

    private String errorTemplate(String retMsg) {
        Dict dict = new Dict().set("retMsg", retMsg);
        return getTemplate("error", dict);
    }

    @Override
    public R<Dict> dowReceipt(List<ReceiptDTO> receiptList) {

        ReceiptDTO receipt = receiptList.get(0);
        String receiptNumber = receipt.getBrSeqNo();

        Entity entity = detailService.getDetailById(receiptNumber);
        String pdfPath = detailService.createPdf(entity);

        String filePath = StrUtil.subBefore(pdfPath, "/", true);
        String fileName = StrUtil.subAfter(pdfPath, "/", true);

        Dict dict = new Dict();
        dict.set("filePath", filePath);
        dict.set("fileName", fileName);

        return R.ok(dict);
    }

    @Override
    public String getActionName(String message) {

        Document document = XmlUtil.readXML(message);
        return Convert.toStr(
                XmlUtil.getByXPath("//BEDC/Message/commHead/tranCode", document, XPathConstants.STRING)
        );
    }

    @Override
    protected String getType(String actionName) {

        ArrayList<String> query = ListUtil.toList(
                QUERY_BALANCE, QUERY_DETAIL, QUERY_PAY_STATUS,
                DOW_RECEIPT, QUERY_PAY, QUERY_PAY_ROLL
        );

        ArrayList<String> pay = ListUtil.toList(
                PAY, PAY_ROLL, SINGLE_PAY_NOT_SAME, SINGLE_PAY_SAME
        );

        if (query.contains(actionName)) {
            return InterfaceTypeEnum.query.getValue();
        } else if (pay.contains(actionName)) {
            return InterfaceTypeEnum.trade.getValue();
        }
        return null;
    }


    private String dowReceipt(String action, Map<String, Object> bodyData) {
        JSONObject head = JSONUtil.parseObj(bodyData.get("commHead"));
        String entSeqNo = head.getStr("entSeqNo");
        String tranDate = head.getStr("tranDate");

        JSONObject body = JSONUtil.parseObj(bodyData.get("Body"));

        String innerNo = body.getStr("uniqueCode");
        Entity entity = detailService.getDetailById(innerNo);

        if (entity == null) {
            return errorTemplate("回单不存在，获取失败");
        }

        // File mkdir = FileUtil.mkdir("/tmp/sklf/cgb/" + IdUtil.getSnowflakeNextIdStr() + "0/");
        String pdfPath = entity.getStr("file_path");
        if (StrUtil.isBlank(pdfPath)) {
            pdfPath = detailService.createPdf(entity);
            log.info("转换完成的PDF文件----------->" + pdfPath);
        }

        byte[] bytesByFile = PdfUtil.getBytesByFile(pdfPath);

        String encodeStr = Base64.encodeStr(bytesByFile, true, false);

        Dict dict = new Dict();
        dict.set("entSeqNo", entSeqNo);
        dict.set("tranDate", tranDate);
        dict.set("receiptNo", innerNo);
        dict.set("fileData", encodeStr);
        return getTemplate(action, dict);
    }
}
